package com.nulldev.util.scripting.ScriptAPI.baseJS.legacy.lexer;

import static com.nulldev.util.scripting.ScriptAPI.baseJS.legacy.lexer.global.BaseJSConstants.UNDEFINED;

import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;

import com.nulldev.util.VariableAPI.util.strings.splitters.Splitters;
import com.nulldev.util.data.Arrays.maps.FastMaps;
import com.nulldev.util.scripting.ScriptAPI.baseJS.legacy.lexer.globals.BaseJSArray;
import com.nulldev.util.scripting.ScriptAPI.baseJS.legacy.lexer.globals.BaseJSConsole;
import com.nulldev.util.scripting.ScriptAPI.baseJS.legacy.lexer.globals.BaseJSEvalFunction;
import com.nulldev.util.scripting.ScriptAPI.baseJS.legacy.lexer.globals.BaseJSMath;;

public class BaseJSEnvironment {
	public final Map<String, BaseJSObject> contents = FastMaps.map();
	private final AtomicReference<Object> return_value = new AtomicReference<Object>(UNDEFINED);
	private final AtomicBoolean enableNewInterpreter = new AtomicBoolean();

	public BaseJSEnvironment() {
		this(false);
	}

	public BaseJSEnvironment(final boolean enableNewInterpreter) {
		this.enableNewInterpreter.lazySet(enableNewInterpreter);
		this.contents.put("Math", new BaseJSMath());
		this.contents.put("Array", new BaseJSArray());
		this.contents.put("console", new BaseJSConsole());
		this.contents.put("eval", new BaseJSEvalFunction());
	}

	public void setReturnValue(final Object r) {
		if (r == null || this.return_value == UNDEFINED)
			return;
		this.return_value.lazySet(r);
	}

	public boolean hasReturnValue() {
		return this.return_value.get() != UNDEFINED;
	}

	public Object getReturnValue() {
		return this.return_value.get();
	}

	public boolean isUsingNewInterpreter() {
		return this.enableNewInterpreter.get();
	}

	public BaseJSEnvironment(final BaseJSObject... o) {
		this();
		for (final BaseJSObject baseJSObject : o) {
			if (baseJSObject == null)
				continue;
			contents.put(baseJSObject.name.trim(), baseJSObject);
		}
	}

	public BaseJSEnvironment(final boolean enableNewInterpreter, final BaseJSObject... o) {
		this(enableNewInterpreter);
		for (final BaseJSObject baseJSObject : o) {
			if (baseJSObject == null)
				continue;
			contents.put(baseJSObject.name.trim(), baseJSObject);
		}
	}

	public BaseJSObject get(final String string) {
		if (string == null || string.isEmpty() || !contents.containsKey(string))
			throw new MissingObjectError(string + " wasn't found in the environment.");
		return contents.get(string);
	}

	@Override
	public String toString() {
		return "BaseJSEnvironment[" + contents + "]";
	}

	public BaseJSFunction getFunction(final String func) {
		final BaseJSObject v = this.get(func);
		if (v == null || (!v.isFunction() && !v.isNativeFunction()))
			return null;
		return ((BaseJSFunction) v);
	}

	public BaseJSObject mGet(final String invokeTarget) {
		BaseJSObject out = null;
		final String[] f = Splitters.splitString(invokeTarget, ".");
		for (final String n : f) {
			if (out == null)
				out = this.get(n);
			else {
				out = out.get(n);
				if (out == null)
					break;
			}
		}
		return out;
	}
}
